﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml;
using Windows.UI.Core;
using System.Text.RegularExpressions;

namespace CompleteReader.Data
{
    /// <summary>
    /// Represents a recently viewed item
    /// </summary>
    public class RecentItem
    {
        /// <summary>
        /// Then name of the document
        /// </summary>
        public string DocumentName { get; private set; }

        /// <summary>
        /// The path where the thumbnail is located
        /// </summary>
        public string ThumbnailLocation { get; private set; }

        public string AdditionalIconLocation { get; private set; }

        public bool HasAdditionalLogo { get { return !string.IsNullOrEmpty(AdditionalIconLocation); } }

        public string Token { get; private set; }

        public string DocumentPath { get; private set; }

        public bool IsProtected { get; private set; }

        public int LastPageNumber { get; set; }

        public RecentItem(string documentName, string thumbLocation, string token, string documentPath, bool isProtected = false)
        {
            this.DocumentName = documentName;
            this.ThumbnailLocation = thumbLocation;
            this.Token = token;
            this.DocumentPath = documentPath;
            this.IsProtected = isProtected;
        }
    }

    /// <summary>
    /// This class represents a collection of recent items the user has accessed.
    /// </summary>
    public class RecentItemsData : System.ComponentModel.INotifyPropertyChanged
    {
        private static RecentItemsData Instance;

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(property));
            }
        }
        
        private static ObservableCollection<RecentItem> _recentItems = null;
        private FrameworkElement _parent;
        public ObservableCollection<RecentItem> RecentItems
        {
            get { return _recentItems; }
        }


        /// <summary>
        /// Creates a new instance of RecentItemsData.
        /// This function will return immediately, so there is no guarantee that the list of recent items
        /// has been populated.
        /// </summary>
        /// <param name="parent">The owner of the recent items list. Used by this class to invoke the dispatcher</param>
        private RecentItemsData()
        {
            return;
        }

        /// <summary>
        /// This functions will always wait until the RecentItems list is populated, and will then return an instance of RecentItemsData.
        /// This should always be the way to get an instance of this class.
        /// </summary>
        /// <param name="parent">The owner of the recent items list. Used by this class to invoke the dispatcher</param>
        /// <returns>An instance of RecentItem guaranteed to have the list of recent items populated.</returns>
        public static async Task<RecentItemsData> GetItemSourceAsync(FrameworkElement parent)
        {
            if (Instance == null)
            {
                Instance = new RecentItemsData();
            }
            Instance._parent = parent;
            _recentItems = await Instance.GetRecentDocumentsAsync();
            return Instance;
        }

        private IAsyncOperation<ObservableCollection<RecentItem>> GetRecentDocumentsAsync()
        {
            Task<ObservableCollection<RecentItem>> t = new Task<ObservableCollection<RecentItem>>(() =>
            {
                return GetRecentDocuments();
            });
            t.Start();
            return t.AsAsyncOperation<ObservableCollection<RecentItem>>();
        }


        private ObservableCollection<RecentItem> GetRecentDocuments()
        {
            try
            {
                ObservableCollection<RecentItem> recentList = new ObservableCollection<RecentItem>();
                AccessListEntryView entries = StorageApplicationPermissions.MostRecentlyUsedList.Entries;
                foreach (AccessListEntry entry in entries)
                {
                    string thumbPath = "";
                    string[] filePathAndFlags = entry.Metadata.Split('/');
                    if (filePathAndFlags.Length > 0)
                    {
                        thumbPath = pdftron.Common.RecentlyUsedCache.GetBitmapPathIfExists(filePathAndFlags[0]);
                    }
                    bool isProtected = FindOptionInFlags("L", filePathAndFlags);
                    if (isProtected)
                    {
                        thumbPath = System.IO.Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "Assets", "DocumentPage", "FileWithoutThumbnailLocked.png");
                    }
                    else if (string.IsNullOrEmpty(thumbPath))
                    {
                        thumbPath = System.IO.Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "Assets", "DocumentPage", "FileWithoutThumbnail.png");
                    }
                    RecentItem item = new RecentItem(System.IO.Path.GetFileName(filePathAndFlags[0]), thumbPath, entry.Token, entry.Metadata);
                    item.LastPageNumber = GetPageNumberFromMetadataString(entry.Metadata);
                    recentList.Add(item);
                }
                return recentList;
            }
            catch (Exception)
            {

            }
            return new ObservableCollection<RecentItem>();
        }

        #region List Management

        /// <summary>
        /// Retrieves the StorageFile associated with the item
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public async Task<StorageFile> GetRecentFileAsync(RecentItem item)
        {
            StorageFile file = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(item.Token);
            return file;
        }

        /// <summary>
        /// Updates the recent list with a file that was selected from the recent list
        /// This means the only work that has to be done is to move it to the top of the list
        /// </summary>
        /// <param name="file"></param>
        /// <param name="isProtected"></param>
        /// <returns>The token for the file</returns>
        public string UpdateWithRecentFile(StorageFile file, bool isProtected = false)
        {
            string fileWithFlags = file.Path;
            if (isProtected)
            {
                fileWithFlags += "/L";
            }
            string token = StorageApplicationPermissions.MostRecentlyUsedList.Add(file, fileWithFlags);
            int listIndex = FindTokenInList(token);
            if (listIndex >= 0)
            {
                if (RecentItems[listIndex].LastPageNumber > 0)
                {
                    StorageApplicationPermissions.MostRecentlyUsedList.Add(file, fileWithFlags + "/p" + RecentItems[0].LastPageNumber);
                }
                RecentItems.RemoveAt(listIndex);
            }
            this.RecentItems.Insert(0, new RecentItem(file.Name, System.IO.Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "Assets", "DocumentPage", "FileWithoutThumbnail.png"), token, file.Path, isProtected));
            return token;
        }

        /// <summary>
        /// Updates the recent list with a file picked from somewhere other than the recent list
        /// </summary>
        /// <param name="file"></param>
        /// <param name="isProtected"></param>
        /// <returns>The token for the file</returns>
        public string UpdateWithNewFile(StorageFile file, bool isProtected = false)
        {
            string fileWithFlags = file.Path;
            if (isProtected)
            {
                fileWithFlags += "/L";
            }
            string token = StorageApplicationPermissions.MostRecentlyUsedList.Add(file, fileWithFlags);
            int listIndex = FindTokenInList(token);
            if (listIndex >= 0)
            {
                if (RecentItems[listIndex].LastPageNumber > 0)
                {
                    StorageApplicationPermissions.MostRecentlyUsedList.Add(file, fileWithFlags + "/p" + RecentItems[0].LastPageNumber);
                }
                RecentItems.RemoveAt(listIndex);
            }
            this.RecentItems.Insert(0, new RecentItem(file.Name, System.IO.Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "Assets", "DocumentPage", "FileWithoutThumbnail.png"), token, file.Path, isProtected));
            return token;
        }

        /// <summary>
        /// Removes all items from recentItems from the list of recent items
        /// </summary>
        /// <param name="recentItems"></param>
        public void RemoveItems(IList<RecentItem> recentItems)
        {
            foreach (RecentItem recentItem in recentItems)
            {
                try
                {
                    StorageApplicationPermissions.MostRecentlyUsedList.Remove(recentItem.Token);
                    this.RecentItems.Remove(recentItem);
                    pdftron.Common.RecentlyUsedCache.RemoveDocument(recentItem.DocumentPath);
                }
                catch (Exception)
                { }
            }
        }

        /// <summary>
        /// Will remove the first recent item which references the storage file if there is any.
        /// </summary>
        /// <param name="file"></param>
        public async void RemoveItem(StorageFile file)
        {
            foreach (RecentItem item in _recentItems)
            {
                try
                {
                    StorageFile fileToCheck = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(item.Token);
                    if (fileToCheck != null)
                    {
                        if (fileToCheck.IsEqual(file))
                        {
                            StorageApplicationPermissions.MostRecentlyUsedList.Remove(item.Token);
                            this.RecentItems.Remove(item);
                            pdftron.Common.RecentlyUsedCache.RemoveDocument(item.DocumentPath);

                            RaisePropertyChanged("RecentItems");

                            return;
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
        }

        /// <summary>
        /// Removes the last entered item in the collection.
        /// </summary>
        public void RemoveLastItem()
        {
            if (_recentItems.Count > 0)
            {
                StorageApplicationPermissions.MostRecentlyUsedList.Remove(_recentItems[0].Token);
                _recentItems.RemoveAt(0);
            }
        }


        /// <summary>
        /// Clears the recent list
        /// </summary>
        public void ClearList()
        {
            // clear the cache
            foreach (RecentItem recentItem in _recentItems)
            {
                pdftron.Common.RecentlyUsedCache.RemoveDocument(recentItem.DocumentPath);
            }
            StorageApplicationPermissions.MostRecentlyUsedList.Clear();
            _recentItems.Clear();
        }


        public async Task UpdatePageAsync(string token, int pageNumber)
        {
            if (StorageApplicationPermissions.MostRecentlyUsedList.Entries.Count == 0)
            {
                return;
            }
            AccessListEntry entry = StorageApplicationPermissions.MostRecentlyUsedList.Entries[0];
            if (!entry.Token.Equals(token))
            {
                return;
            }

            string metadata = entry.Metadata;

            //string[] filePathAndFlags = entry.Metadata.Split('/');
            Regex pageNumberRegex = new Regex(@"/p\d+");
            string pageNumberString = "/p" + pageNumber;

            Match match = pageNumberRegex.Match(metadata);
            if (match.Success)
            {
                metadata = pageNumberRegex.Replace(metadata, pageNumberString);
            }
            else
            {
                metadata += pageNumberString;
            }

            entry.Metadata = metadata;
            StorageFile fileToUpdate = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(token);

            string token2 = StorageApplicationPermissions.MostRecentlyUsedList.Add(fileToUpdate, metadata);

        }

        public int GetPageNumberFromToken(string token)
        {
            if (StorageApplicationPermissions.MostRecentlyUsedList.Entries.Count == 0)
            {
                return -1;
            }
            AccessListEntry entry = StorageApplicationPermissions.MostRecentlyUsedList.Entries[0];
            if (!entry.Token.Equals(token))
            {
                return -1;
            }

            string metadata = entry.Metadata;

            return GetPageNumberFromMetadataString(entry.Metadata);
        }

        #endregion List Management


        #region Utility Functions

        /// <summary>
        /// Returns the index of the list item which contains token
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        private int FindTokenInList(string token)
        {
            int listSize = this.RecentItems.Count;
            for (int i = 0; i < listSize; i++)
            {
                RecentItem item = RecentItems[i];
                if (item.Token.Equals(token, StringComparison.Ordinal))
                {
                    return i;
                }
            }
            return -1;
        }

        private bool FindOptionInFlags(string option, string[] flags)
        {
            int numOptions = flags.Length;
            for (int i = 1; i < numOptions; i++)
            {
                if (flags[i].Equals(option, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        private int GetPageNumberFromMetadataString(string metadata)
        {
            int result = -1;

            try
            {
                Regex pageNumberRegex = new Regex(@"/p\d+");
                Match match = pageNumberRegex.Match(metadata);

                if (match.Success)
                {
                    Regex justNMumberRegex = new Regex(@"\d+");
                    Match pgMatch = justNMumberRegex.Match(match.Groups[0].ToString());
                    if (pgMatch.Success)
                    {
                        Int32.TryParse(pgMatch.Groups[0].ToString(), out result);
                    }
                }
            }
            catch (Exception)
            {

            }
            return result;
        }

        #endregion Utility Functions
    }
}
